﻿using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.ComponentModel;
using System.Linq.Dynamic.Core;
using System.Reflection;

namespace CTP.CRM.Core.Swagger.Filter
{
    internal class SwaggerEnumDocumentFilter : IDocumentFilter
    {
        private static List<string> _assemblyNames;

        private static List<TypeInfo> _allEnumTypes = new List<TypeInfo>();

        public SwaggerEnumDocumentFilter(List<string> assemblyNames)
        {
            _assemblyNames = assemblyNames.ToList();
            foreach (string assemblyName in _assemblyNames)
            {
                IEnumerable<Type> enumerable = from m in Assembly.Load(assemblyName).GetTypes()
                                               where m.IsEnum
                                               select m;
                foreach (Type item in enumerable)
                {
                    if (!_allEnumTypes.Any((TypeInfo m) => m.GUID == item.GUID))
                    {
                        _allEnumTypes.Add(item.GetTypeInfo());
                    }
                }
            }
        }

        public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        {
            foreach (KeyValuePair<string, OpenApiSchema> schema in swaggerDoc.Components.Schemas)
            {
                OpenApiSchema value = schema.Value;
                if (value.Enum == null || value.Enum.Count <= 0)
                {
                    continue;
                }

                List<OpenApiInteger> list = new List<OpenApiInteger>();
                foreach (IOpenApiAny item in value.Enum)
                {
                    list.Add((OpenApiInteger)item);
                }

                Type typeByName = GetTypeByName(schema.Value, schema.Key);
                value.Description += DescribeEnum(typeByName, list);
            }
        }

        //
        // 摘要:
        //     根据属性名称获取具体类型
        //
        // 参数:
        //   prop:
        //
        //   name:
        private Type GetTypeByName(OpenApiSchema prop, string name)
        {
            string name2 = name;
            List<TypeInfo> list = _allEnumTypes.Where((TypeInfo m) => m.Name == name2)?.ToList();
            if (list.Count() == 1)
            {
                return list.First().AsType();
            }

            foreach (TypeInfo item in list)
            {
                Array values = Enum.GetValues(item.GetType());
                if (prop.Enum.Count != values.Length)
                {
                    continue;
                }

                foreach (IOpenApiAny item2 in prop.Enum)
                {
                    object value = item2.GetType().GetProperty("Value").GetValue(item2);
                    if (!values.ToDynamicList().Any((dynamic m) => (int)m == (int)value))
                    {
                        return null;
                    }
                }

                return item.GetType();
            }

            return null;
        }

        //
        // 摘要:
        //     获取每个枚举项上的描述信息
        //
        // 参数:
        //   type:
        //
        //   enums:
        private static string DescribeEnum(Type type, List<OpenApiInteger> enums)
        {
            List<string> list = new List<string>();
            foreach (OpenApiInteger @enum in enums)
            {
                if (!(type == null))
                {
                    object value = Enum.Parse(type, @enum.Value.ToString());
                    DescriptionAttribute customAttribute = @enum.GetType().GetCustomAttribute<DescriptionAttribute>();
                    if (string.IsNullOrEmpty(customAttribute?.Description))
                    {
                        list.Add($"{Enum.GetName(type, value)}:{@enum.Value}; ");
                    }
                    else
                    {
                        list.Add($"{Enum.GetName(type, value)}:{@enum.Value}({customAttribute}); ");
                    }
                }
            }

            return "<br/>&nbsp;&nbsp;&nbsp;&nbsp;" + string.Join("<br/>&nbsp;&nbsp;&nbsp;&nbsp;", list);
        }
    }
}
